#include "lv_qrcode.h"

#ifdef USE_LV_QRCODE

#include <stdio.h>
#include <stdlib.h>
#include "lvgl/src/lv_widgets/lv_canvas.h"
#include "lvgl/src/lv_core/lv_disp.h"
#include "lvgl/src/lv_draw/lv_img_cache.h"
#include "qrencode.h"
#include "qrspec.h"
#include "lv_drivers/display/scaler.h"

#define QR_STD_VERSION  0

static lv_signal_cb_t ancestor_signal;

static lv_res_t qrcode_signal(lv_obj_t * img, lv_signal_t sign, void * param)
{
    lv_res_t res;

    /* Include the ancient signal function */
    res = ancestor_signal(img, sign, param);
    if(res != LV_RES_OK) return res;

    if(sign == LV_SIGNAL_CLEANUP) {
        lv_img_dsc_t *img_des = (lv_img_dsc_t *)lv_img_get_src(img);
        if(img_des != NULL) {
            lv_img_cache_invalidate_src(img_des);
            lv_mem_aligned_free((void *)img_des->data);
            lv_mem_free(img_des);
        }
    }

    return res;
}

lv_obj_t *qrcode_gen_with_color(lv_obj_t * par, const char *cid, int px, int py, int width, lv_color_t color)
{
    int src_buf_size, dst_buf_size, qr_w, idx;
    int qr_pixel_width = 1;
    int version = QR_STD_VERSION;
    unsigned char *qr_data;
    QRcode *qrcode;
    int z;

    if(par == NULL) par = lv_disp_get_scr_act(NULL);
    qrcode = QRcode_encodeString(cid, version, QR_ECLEVEL_L, QR_MODE_8, 1);
    if ((!qrcode) || (qrcode->width <= 0)) {
        printf("QRcode_encodeString failed\n");
        return NULL;
    }

    qr_w = qrcode->width;
    if(width > qr_w) {
        qr_pixel_width = width / qr_w;
    }

    src_buf_size = qr_w * qr_w * qr_pixel_width * qr_pixel_width * sizeof(lv_color_t);
    dst_buf_size = width * width * 2;

    lv_color_t *orig_color_map = lv_mem_aligned_alloc(src_buf_size, 32);
    if(orig_color_map == NULL) {
        printf("qrcode_gen, no memory 1!!\n");
        goto err;
    }

    lv_color_t * pcolor = orig_color_map;
    for(z = 0; z < src_buf_size/sizeof(lv_color_t); z++) {
        memcpy(pcolor++, &color, sizeof(lv_color_t));
    }

    qr_data = qrcode->data;
    printf("qr width: %d\n", qrcode->width);
    int p1;
    for (int y = 0; y < qr_w; y++) {
        for (int x = 0; x < qr_w; x++) {
            idx = y*qr_w + x;
            p1 = y * qr_w * (qr_pixel_width * qr_pixel_width) + x * qr_pixel_width;
            if ((qr_data[idx] & 0x1) == 0) { /* white pix */
                int i = 0;
                int p2 = p1;
                for(i = 0; i < qr_pixel_width; i++) {
                    memset(&orig_color_map[p2], 0xff, sizeof(lv_color_t) * qr_pixel_width);
                    p2 += (qr_w * qr_pixel_width);
                }
            }
        }
    }

    lv_color_t *dst_color_map = lv_mem_aligned_alloc(dst_buf_size, 32);
    if(dst_color_map == NULL) {
        printf("qrcode_gen, no memory 2!!\n");
        lv_mem_aligned_free(orig_color_map);
        goto err;
    }

    if(rgb565_scale(qr_w * qr_pixel_width, qr_w * qr_pixel_width, width, width, (uint8_t *)orig_color_map, (uint8_t *)dst_color_map) == 0) {
        lv_mem_aligned_free(orig_color_map);
        lv_mem_aligned_free(dst_color_map);
        goto err;
    }

    lv_img_dsc_t *img_des = lv_mem_alloc(sizeof(lv_img_dsc_t));
    if(img_des == NULL) {
        printf("qrcode_gen, no memory 3!!\n");
        lv_mem_aligned_free(orig_color_map);
        lv_mem_aligned_free(dst_color_map);
        goto err;
    }
    lv_img_cache_invalidate_src(img_des);
    memset(img_des, 0 , sizeof(lv_img_dsc_t));
    img_des->header.w = width;
    img_des->header.h = width;
    img_des->header.cf = LV_IMG_CF_TRUE_COLOR;
    img_des->data_size = dst_buf_size;
    img_des->data = (const uint8_t *)dst_color_map;

    lv_obj_t *img = lv_img_create(par, NULL);
    lv_img_set_src(img, img_des);

    lv_obj_set_pos(img, px, py);
    if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_cb(img);
    /*The signal and design functions are not copied so set them here*/
    lv_obj_set_signal_cb(img, qrcode_signal);

    QRcode_free(qrcode);
    lv_mem_aligned_free(orig_color_map);
    return img;
err:
    QRcode_free(qrcode);
    printf("qrcode_gen failed\n");
    return NULL;
}

lv_obj_t *qrcode_gen(lv_obj_t * par, const char *cid, int px, int py, int width)
{
    return qrcode_gen_with_color(par, cid, px, py, width, LV_COLOR_BLACK);
}

#endif
